package BD;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLNonTransientConnectionException;
import java.util.ArrayList;
import java.util.logging.Level;
import org.apache.log4j.Logger;

public class MySql<T> {

    private static String user, pw, dataBaseName, host, url,
            //  private String driver = "com.mysql.jdbc.Driver";
            driver = "org.apache.derby.jdbc.ClientDriver";
    private Connection conexion = null;
    private PreparedStatement sentencia = null;
    private static Logger logger = Logger.getLogger(MySql.class);

    public void testearConexion() throws MyException {
        try {
            conexion = DriverManager.getConnection(url, user, pw);
        } catch (SQLNonTransientConnectionException e) {
            logger.error("Error de conexion: autenticación no valida o servidor no iniciado", e);
            throw new MyException("Error de conexion: autenticación no valida o servidor no iniciado.", e);
        } catch (SQLException e) {
            logger.error("Error de SQL", e);
            throw new MyException("Error de SQL", e);
        } finally {
            if (conexion != null) {
                try {
                    conexion.close();
                } catch (SQLException e) {
                    logger.error("Error al cerrar conexion", e);
                    throw new MyException("Error de SQL", e);
                }
            }
        }
    }

    public static void cargarArchivo() throws MyException {
        CargarConfig c = new CargarConfig();
        ConfiguracionMySql datosConexion = c.LeerArchivo();
        try {
            user = datosConexion.getUser();
            pw = datosConexion.getPw();
            dataBaseName = datosConexion.getDataBaseName();
            host = datosConexion.getHost();
        } catch (NullPointerException e) {
            logger.error("Archivo de configuracion no encontrado", e);
            throw new MyException("Archivo de configuracion no encontrado", e);
        }
        url = "jdbc:derby://" + host + "/" + dataBaseName;
    }

    public MySql() throws MyException {

        //
        //        cCfg = new CargarConfig();
        //        ConfiguracionMySql cfg = cCfg.LeerArchivo();
        //
        //        try {
        //            this.user = cfg.getUser();
        //            pw = cfg.getPw();
        //            dataBaseName = cfg.getDataBaseName();
        //            host = cfg.getHost();
        //        } catch (NullPointerException e) {
        //            logger.error("Archivo de configuracion no encontrado", e);
        //            throw new MyException("Archivo de configuracion no encontrado", e);
        //        }
        //        //url = "jdbc:mysql://" + host + "/" + dataBaseName;
        //        url = "jdbc:derby://" + host + "/" + dataBaseName;
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            logger.error("Error al cargar Driver.", e);
            throw new MyException("Error al cargar Driver.", e);
        }
    }

    public List<T> executeQuery(String sql, Class clase, Object[] parametros) throws MyException {
        List<T> listaDeObjetos = new ArrayList<T>();
        try {
            ResultSet filas;
            conexion = DriverManager.getConnection(url, user, pw);
            sentencia = conexion.prepareStatement(sql);
            if (parametros != null) {
                for (int i = 1; i < parametros.length + 1; i++) {
                    sentencia.setObject(i, parametros[i - 1]);
                }
            }
            filas = sentencia.executeQuery();//obtiene el resultset de la consulta

            while (filas.next()) {
                T objeto = (T) Class.forName(clase.getName()).newInstance(); //crea un objeto correspondiente a su clase
                Method[] metodos = objeto.getClass().getDeclaredMethods();//instancia todos los metodos de la clase
                for (int i = 0; i < metodos.length; i++) {
                    if (metodos[i].getName().startsWith("set")) {//si el metodo es un set->
                        metodos[i].invoke(objeto, filas.getObject(metodos[i].getName().substring(3)));
                        //metodos[i].getName().substring(3)) da el nombre de la columna en la BD
                        //se invoca el metodo set, pasandole como atributo el valor del resultset
                    }
                }
                listaDeObjetos.add(objeto);
            }

            filas.close();
        } catch (ClassNotFoundException e) {
            logger.error("Error al seleccionar registros.", e);
            throw new MyException("Error al seleccionar registros.", e);
        } 
        catch (  InstantiationException  e) {
            logger.error("Error al seleccionar registros.", e);
            throw new MyException("Error al seleccionar registros.", e);
        } 
        catch ( IllegalAccessException  e) {
            logger.error("Error al seleccionar registros.", e);
            throw new MyException("Error al seleccionar registros.", e);
        } 
        catch ( SecurityException e) {
            logger.error("Error al seleccionar registros.", e);
            throw new MyException("Error al seleccionar registros.", e);
        } 
        catch (  IllegalArgumentException  e) {
            logger.error("Error al seleccionar registros.", e);
            throw new MyException("Error al seleccionar registros.", e);
        } 
        catch ( InvocationTargetException e) {
            logger.error("Error al seleccionar registros.", e);
            throw new MyException("Error al seleccionar registros.", e);
        } catch (SQLNonTransientConnectionException e) {
            logger.error("**************El servidor no esta iniciado!!!! INICIELO !!!!**************", e);
            throw new MyException("El servidor no esta iniciado.", e);
        } catch (SQLException e) {
            logger.error("Error de SQL", e);
            throw new MyException("Error de SQL", e);
        } finally {
            if (sentencia != null) {
                try {
                    sentencia.close();
                } catch (SQLException e) {
                    logger.error("Error al cerrar sentencia", e);
                    throw new MyException("Error de SQL", e);
                }
            }
            if (conexion != null) {
                try {
                    conexion.close();
                } catch (SQLException e) {
                    logger.error("Error al cerrar conexion", e);
                    throw new MyException("Error de SQL", e);
                }
            }
        }
        return listaDeObjetos;
    }

    public int executeUpdate(String sql, Object[] parametros) throws MyException {

        int filasAfectadas = 0;
        try {
            Class.forName(driver);
            conexion = DriverManager.getConnection(url, user, pw);
            sentencia = conexion.prepareStatement(sql);

            if (parametros == null) {
                logger.error("Parametros es null");
                throw new MyException("Parametros es null");

            } else {
                for (int i = 1; i < parametros.length + 1; i++) {
                    sentencia.setObject(i, parametros[i - 1]);
                }
            }

            filasAfectadas = sentencia.executeUpdate();
        } catch (ClassNotFoundException e) {
            logger.error("Error driver", e);
            throw new MyException("Error driver", e);
        } catch (SQLException e) {
            logger.error("Error de SQL", e);
            throw new MyException("Error de SQL", e);
        } finally {
            if (sentencia != null) {
                try {
                    sentencia.close();
                } catch (SQLException e) {
                    logger.error("Error al cerrar sentencia", e);
                    throw new MyException("Error de SQL", e);
                }
            }
            if (conexion != null) {
                try {
                    conexion.close();
                } catch (SQLException e) {
                    logger.error("Error al cerrar conexion", e);
                    throw new MyException("Error de SQL", e);
                }
            }
        }
        return filasAfectadas;
    }
}
